<?php 

/**
* 微信H5支付自写类
*
* 此类只编写了H5支付的必填参数
*/
class wxPay{
	//四个参数的配置信息
	// define('APPID','wx2de38da107527240');
	// define('MCHID','1296617901');
	// define('KEY','highglass112358highglass11235811');
	// define('APPSECRET','446fdd6335e31f0ceeaf63562ff03a4c');

	public $appid = 'wx2de38da107527240';
	public $mchid = '1296617901';
	public $key = 'highglass112358highglass11235811';
	public $appsecret = '446fdd6335e31f0ceeaf63562ff03a4c';

	private $values = array();

	private $result = array();

	//设置商品描述
	public function SetBody($value){
		$this->values['body'] = $value;
	}

	//设置商户订单号
	public function SetOut_trade_no($value){
		$this->values['out_trade_no'] = $value;
	}

	//设置总金额
	public function SetTotal_fee($value){
		$this->values['total_fee'] = $value;
	}

	//设置微信支付异步回调地址
	public function SetNotify_url($value){
		$this->values['notify_url'] = $value;
	}

	//设置交易类型
	public function SetTrade_type($value){
		$this->values['trade_type'] = $value;
	}

	//设置用户唯一标识
	public function SetOpenid($value){
		$this->values['openid'] = $value;
	}

	//设置appid
	public function SetAppid(){
		$this->values['appid'] = $this->appid;
	}

	//设置mchid
	public function SetMch_id(){
		$this->values['mch_id'] = $this->mchid;
	}

	//设置终端IP
	public function SetSpbill_create_ip($value){
		$this->values['spbill_create_ip'] = $value;
	}

	//设置随机数
	public function SetNonce_str(){
		$nonce_str = $this->GetNonce_str();
		$this->values['nonce_str'] = $nonce_str;
	}

	//设置签名 
	public function SetSign(){
		$string = $this->MakeSign();
		$this->values['sign'] = $string;
	}

	//设置场景信息
	public function SetScene_info(){
		$scene_info = '{"h5_info":{"type": "Wap","wap_url": "https://pay.hispot.cn","wap_name": "hispot"}}';
		$this->values['scene_info'] = $scene_info;
	}

	//生成签名
	public function MakeSign(){
		//签名步骤一：字典序排序，并循环拼接为字符串形式
		ksort($this->values);
		$string = $this->ToUrlParams();
		//签名步骤二：在拼接好的字符串后面加上KEY
		$string = $string .= "&key=".$this->key;
		//签名步骤三：将拼接好的字符串MD5加密
		$string = MD5($string);
		//签名步骤四：将字符串里面的字符转换为大写
		$string = strtoupper($string);
		return $string;
	}

	//循环排序拼接成url的格式
	public function ToUrlParams(){
		$buff = '';
		foreach ($this->values as $k => $v) {
			if ($k != 'sign' && $v != '' && !is_array($v)) {
				$buff .= $k . '=' . $v . '&';
			}
		}
		$buff = trim($buff,'&');
		return $buff;
	}

	//获取随机字符串 长度不差过32位
	public function GetNonce_str($length = 32){
		$chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
		$str ="";
		for ( $i = 0; $i < $length; $i++ )  {  
			$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
		} 
		return $str;
	}

	//判断必填参数body是否设置
	public function IsBodySet(){
		if (array_key_exists('body',$this->values)) {
			return true;
		}else{
			return false;
		}
	}

	//判断必填参数out_trade_no是否设置
	public function IsOut_trade_noSet(){
		if (array_key_exists('out_trade_no',$this->values)) {
			return true;
		}else{
			return false;
		}
	}

	//判断必填参数total_fee是否设置
	public function IsTotal_feeSet(){
		if (array_key_exists('total_fee',$this->values)) {
			return true;
		}else{
			return false;
		}
	}

	//判断必填参数trade_type是否设置
	public function IsTrade_typeSet(){
		if (array_key_exists('trade_type',$this->values)) {
			return true;
		}else{
			return false;
		}
	}

	//判断必填参数notify_url是否设置
	public function IsNotify_urlSet(){
		if (array_key_exists('total_fee',$this->values)) {
			return true;
		}else{
			return false;
		}
	}

	//判断参数openid是否设置
	public function IsOpenidSet(){
		if (array_key_exists('openid',$this->values)) {
			return true;
		}else{
			return false;
		}
	}
	
	//循环处理数组输出XML格式
	public function ToXml()
	{
		if(!is_array($this->values) 
			|| count($this->values) <= 0)
		{
    		return "数组数据异常！";
    	}
    	
    	$xml = "<xml>";
    	foreach ($this->values as $key=>$val)
    	{
    		if (is_numeric($val)){
    			$xml.="<".$key.">".$val."</".$key.">";
    		}else{
    			$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    		}
        }
        $xml.="</xml>";
        return $xml; 
	}

	//统一下单方法
	public function unifiedOrder(){
		//判断必填参数是否存在
		if (!$this->IsBodySet()) {
			return "缺少统一支付接口必填参数body！";
		}else if(!$this->IsOut_trade_noSet()){
			return "缺少统一支付接口必填参数out_trade_no！";
		}else if(!$this->IsTotal_feeSet()){
			return "缺少统一支付接口必填参数total_fee！";
		}else if(!$this->IsTrade_typeSet()){
			return "缺少统一支付接口必填参数trade_type！";
		}else if(!$this->IsNotify_urlSet()){
			return "缺少统一支付接口必填参数notify_url！";
		}

		//统一下单接口url
		$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

		//补全其他参数
		$this->SetAppid();	//设置Appid
		$this->SetMch_id();	//设置mch_id
		$this->SetSpbill_create_ip($_SERVER['REMOTE_ADDR']);  //设置终端IP
		$this->SetNonce_str();	//设置随机数
		$this->SetScene_info();	//设置支付场景信息
		$this->SetSign();	//设置签名

		//获取xml格式
		$xml = $this->ToXml();

		//请求接口下单
		$response = $this->postXmlCurl($xml, $url);

		//将xml格式的数据转换为数组
		$result = $this->xml_to_data($response);

		return $result;
	}

	//利用curl以post方式提交到对应的接口url
	public function postXmlCurl($xml, $url, $second = 30){	
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
	
		curl_setopt($ch,CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
		//设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		//要求结果为字符串且输出到屏幕上
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	
		//post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		//运行curl
		$data = curl_exec($ch);
		//返回结果
		if($data){
			curl_close($ch);
			return $data;
		} else { 
			$error = curl_errno($ch);
			curl_close($ch);
			return "curl出错，错误码:$error";
		}
	}

	//将xml格式转换为数组
	public function xml_to_data($xml){
        if (!$xml) {
            return 'xml数据不正确！';
        }
        //将XML转为array
        //禁止引用外部xml实体
        $disableLibxmlEntityLoader = libxml_disable_entity_loader(true);
        // libxml_disable_entity_loader(true);
        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        libxml_disable_entity_loader($disableLibxmlEntityLoader);
        return $data;
    }

    //获取JSAPI拉起界面所需参数
    public function GetJsApiParameters($UnifiedOrderResult){
    	if(!array_key_exists("appid", $UnifiedOrderResult)
		|| !array_key_exists("prepay_id", $UnifiedOrderResult)
		|| $UnifiedOrderResult['prepay_id'] == ""){
			return "参数错误";
		}
		//设置参数
		$this->SetTimeStamp(time());
		$this->AgainSetAppId($UnifiedOrderResult['appid']);
		$this->SetNonceStr($this->GetNonce_str());
		$this->SetPackage("prepay_id=" . $UnifiedOrderResult['prepay_id']);
		$this->SetSignType('MD5');
		$this->SetPaySign($this->AgainMakeSign());
		$parameters = json_encode($this->result);
		return $parameters;
    }

    //设置JSAPI拉起界面所需参数  开始位置302  注意jsapi所需参数格式全部为string类型
    //设置时间戳
    public function SetTimeStamp($value){
    	$this->result['timeStamp'] = (string)$value;
    }

    //设置公众号ID
    public function AgainSetAppId($value){
    	$this->result['appId'] = $value;
    }

    //设置随机字符串
    public function SetNonceStr($value){
    	$this->result['nonceStr'] = $value;
    }

    //设置订单详情扩展字符串
    public function SetPackage($value){
    	$this->result['package'] = $value;
    }

    //设置签名方式
    public function SetSignType($value){
    	$this->result['signType'] = $value;
    }

    //设置签名
    public function SetPaySign($value){
    	$this->result['paySign'] = $value;
    }

    //生成二次签名 此次签名为下单成功之后根据返回参数中的必要参数进行签名
    public function AgainMakeSign(){
    	//签名步骤一：字典序排序，并循环拼接为字符串形式
		ksort($this->result);
		$string = '';
		foreach ($this->result as $k => $v) {
			if ($k != 'sign' && $v != '' && !is_array($v)) {
				$string .= $k . '=' . $v . '&';
			}
		}
		$string = trim($string,'&');
		//签名步骤二：在拼接好的字符串后面加上KEY
		$string = $string .= "&key=".$this->key;
		//签名步骤三：将拼接好的字符串MD5加密
		$string = MD5($string);
		//签名步骤四：将字符串里面的字符转换为大写
		$string = strtoupper($string);
		return $string;
    }

    //接收回调数据
    public function getCallbackData(){
    	//获取通知的数据
        $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
        $data = array();
        if (empty($xml)) {
            return false;
        }
        $data = $this->xml_to_data($xml);
        if (!empty($data['return_code'])) {
            if ($data['return_code'] == 'FAIL') {
                return false;
            }
        }
        return $data;
    }

    //三次生成签名   此次签名为支付成功之后回调之后参数签名
    public function ThreeMakeSign($value){
    	//签名步骤一：字典序排序，并循环拼接为字符串形式
		ksort($value);
		$string = '';
		foreach ($value as $k => $v) {
			if ($k != 'sign' && $v != '' && !is_array($v) && $k != 'result_code' && $k != 'return_code') {
				$string .= $k . '=' . $v . '&';
			}
		}
		$string = trim($string,'&');
		//签名步骤二：在拼接好的字符串后面加上KEY
		$string = $string .= "&key=".$this->key;
		//签名步骤三：将拼接好的字符串MD5加密
		$string = MD5($string);
		//签名步骤四：将字符串里面的字符转换为大写
		$string = strtoupper($string);
		return $string;
    }
}
 ?>
